/*-------------------------------------------------------------------------
 *
 * deparse_statistics_stmts.c
 *	  All routines to deparse statistics statements.
 *	  This file contains all entry points specific for statistics statement deparsing
 *    as well as functions that are currently only used for deparsing of the statistics
 *    statements.
 *
 * Copyright (c) Citus Data, Inc.
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/namespace.h"
#include "lib/stringinfo.h"
#include "nodes/nodes.h"
#include "utils/builtins.h"

#include "pg_version_constants.h"

#include "distributed/citus_ruleutils.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/relay_utility.h"

/** the following declation is just for fix compiling error  */
/* ----------------------
 *		Create Statistics Statement
 * ----------------------
 */
typedef struct CreateStatsStmt {
    NodeTag type;
    List* defnames;     /* qualified name (list of Value strings) */
    List* stat_types;   /* stat types (list of Value strings) */
    List* exprs;        /* expressions to build statistics on */
    List* relations;    /* rels to build stats on (list of RangeVar) */
    bool if_not_exists; /* do nothing if stats name already exists */
} CreateStatsStmt;

/* ----------------------
 *		Alter Statistics Statement
 * ----------------------
 */
typedef struct AlterStatsStmt {
    NodeTag type;
    List* defnames;      /* qualified name (list of String) */
    Node* stxstattarget; /* statistics target */
    bool missing_ok;     /* skip error if statistics object is missing */
} AlterStatsStmt;

/*
 * StatsElem - statistics parameters (used in CREATE STATISTICS)
 *
 * For a plain attribute, 'name' is the name of the referenced table column
 * and 'expr' is NULL.  For an expression, 'name' is NULL and 'expr' is the
 * expression tree.
 */
typedef struct StatsElem {
    NodeTag type;
    char* name; /* name of attribute to index, or NULL */
    Node* expr; /* expression to index, or NULL */
} StatsElem;

static void AppendCreateStatisticsStmt(StringInfo buf, CreateStatsStmt* stmt);
static void AppendDropStatisticsStmt(StringInfo buf, List* nameList, bool ifExists);
static void AppendAlterStatisticsRenameStmt(StringInfo buf, RenameStmt* stmt);
static void AppendAlterStatisticsSchemaStmt(StringInfo buf, AlterObjectSchemaStmt* stmt);
static void AppendAlterStatisticsStmt(StringInfo buf, AlterStatsStmt* stmt);
static void AppendAlterStatisticsOwnerStmt(StringInfo buf, AlterOwnerStmt* stmt);
static void AppendStatisticsName(StringInfo buf, CreateStatsStmt* stmt);
static void AppendStatTypes(StringInfo buf, CreateStatsStmt* stmt);
static void AppendColumnNames(StringInfo buf, CreateStatsStmt* stmt);
static void AppendTableName(StringInfo buf, CreateStatsStmt* stmt);

char* DeparseCreateStatisticsStmt(Node* node)
{
    CreateStatsStmt* stmt = castNode(CreateStatsStmt, node);

    StringInfoData str;
    initStringInfo(&str);

    AppendCreateStatisticsStmt(&str, stmt);

    return str.data;
}

char* DeparseDropStatisticsStmt(List* nameList, bool ifExists)
{
    StringInfoData str;
    initStringInfo(&str);

    AppendDropStatisticsStmt(&str, nameList, ifExists);

    return str.data;
}

char* DeparseAlterStatisticsRenameStmt(Node* node)
{
    RenameStmt* stmt = castNode(RenameStmt, node);

    StringInfoData str;
    initStringInfo(&str);

    AppendAlterStatisticsRenameStmt(&str, stmt);

    return str.data;
}

char* DeparseAlterStatisticsSchemaStmt(Node* node)
{
    AlterObjectSchemaStmt* stmt = castNode(AlterObjectSchemaStmt, node);

    StringInfoData str;
    initStringInfo(&str);

    AppendAlterStatisticsSchemaStmt(&str, stmt);

    return str.data;
}

char* DeparseAlterStatisticsStmt(Node* node)
{
    AlterStatsStmt* stmt = castNode(AlterStatsStmt, node);

    StringInfoData str;
    initStringInfo(&str);

    AppendAlterStatisticsStmt(&str, stmt);

    return str.data;
}

char* DeparseAlterStatisticsOwnerStmt(Node* node)
{
    AlterOwnerStmt* stmt = castNode(AlterOwnerStmt, node);
#ifdef DISABLE_OG_COMMENTS
    Assert(stmt->objectType == OBJECT_STATISTIC_EXT);
#endif
    StringInfoData str;
    initStringInfo(&str);

    AppendAlterStatisticsOwnerStmt(&str, stmt);

    return str.data;
}

static void AppendCreateStatisticsStmt(StringInfo buf, CreateStatsStmt* stmt)
{
    appendStringInfoString(buf, "CREATE STATISTICS ");

    if (stmt->if_not_exists) {
        appendStringInfoString(buf, "IF NOT EXISTS ");
    }

    AppendStatisticsName(buf, stmt);

    AppendStatTypes(buf, stmt);

    appendStringInfoString(buf, " ON ");

    AppendColumnNames(buf, stmt);

    appendStringInfoString(buf, " FROM ");

    AppendTableName(buf, stmt);
}

static void AppendDropStatisticsStmt(StringInfo buf, List* nameList, bool ifExists)
{
    appendStringInfoString(buf, "DROP STATISTICS ");

    if (ifExists) {
        appendStringInfoString(buf, "IF EXISTS ");
    }

    appendStringInfo(buf, "%s", NameListToQuotedString(nameList));
}

static void AppendAlterStatisticsRenameStmt(StringInfo buf, RenameStmt* stmt)
{
    appendStringInfo(buf, "ALTER STATISTICS %s RENAME TO %s",
                     NameListToQuotedString((List*)stmt->object),
                     quote_identifier(stmt->newname));
}

static void AppendAlterStatisticsSchemaStmt(StringInfo buf, AlterObjectSchemaStmt* stmt)
{
    appendStringInfo(buf, "ALTER STATISTICS %s SET SCHEMA %s",
                     NameListToQuotedString((List*)stmt->object),
                     quote_identifier(stmt->newschema));
}

static void AppendAlterStatisticsStmt(StringInfo buf, AlterStatsStmt* stmt)
{
#ifdef DISABLE_OG_COMMENTS
    appendStringInfo(buf, "ALTER STATISTICS %s SET STATISTICS %d",
                     NameListToQuotedString(stmt->defnames),
                     getIntStxstattarget_compat(stmt->stxstattarget));
#endif
    Assert(0);
    ereport(ERROR, (errmsg("Not supported in distributed openGauss currently")));
}

static void AppendAlterStatisticsOwnerStmt(StringInfo buf, AlterOwnerStmt* stmt)
{
    List* names = (List*)stmt->object;
    appendStringInfo(buf, "ALTER STATISTICS %s OWNER TO %s",
                     NameListToQuotedString(names), RoleSpecString(stmt->newowner, true));
}

static void AppendStatisticsName(StringInfo buf, CreateStatsStmt* stmt)
{
    List* schemaNameVal = (List*)linitial(stmt->defnames);
    const char* schemaName = quote_identifier(strVal(schemaNameVal));

    List* statNameVal = (List*)lsecond(stmt->defnames);
    const char* statName = quote_identifier(strVal(statNameVal));

    appendStringInfo(buf, "%s.%s", schemaName, statName);
}

static void AppendStatTypes(StringInfo buf, CreateStatsStmt* stmt)
{
    if (list_length(stmt->stat_types) == 0) {
        return;
    }

    appendStringInfoString(buf, " (");

    List* statType = NULL;
    foreach_declared_ptr(statType, stmt->stat_types)
    {
        appendStringInfoString(buf, strVal(statType));

        if (statType != llast(stmt->stat_types)) {
            appendStringInfoString(buf, ", ");
        }
    }

    appendStringInfoString(buf, ")");
}

static void AppendColumnNames(StringInfo buf, CreateStatsStmt* stmt)
{
    StatsElem* column = NULL;

    foreach_declared_ptr(column, stmt->exprs)
    {
        if (!column->name) {
            ereport(
                ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg(
                     "only simple column references are allowed in CREATE STATISTICS")));
        }

        const char* columnName = quote_identifier(column->name);

        appendStringInfoString(buf, columnName);

        if (column != llast(stmt->exprs)) {
            appendStringInfoString(buf, ", ");
        }
    }
}

static void AppendTableName(StringInfo buf, CreateStatsStmt* stmt)
{
    /* statistics' can be created with only one relation */
    Assert(list_length(stmt->relations) == 1);
    RangeVar* relation = (RangeVar*)linitial(stmt->relations);
    char* relationName = relation->relname;
    char* schemaName = relation->schemaname;

    appendStringInfoString(buf, quote_qualified_identifier(schemaName, relationName));
}
